﻿using System;

namespace Utils
{
    public abstract class TimeredEngineBase : PropertyChangedNotifier, IDisposable
    {
        #region Attributes

        private readonly int _interval;
        private bool _started;
        private bool _stopped;
        private readonly object _stopLock;

        #endregion

        #region Properties

        protected Timer2 Timer { get; set; }

        public bool Stopped
        {
            get
            {
                return _stopped;
            }
            protected set
            {
                if (value != _stopped)
                {
                    _stopped = value;
                    base.OnPropertyChanged("Stopped");
                }
            }
        }

        public bool Started
        {
            get
            {
                return _started;
            }
            protected set
            {
                if (value != _started)
                {
                    _started = value;
                    base.OnPropertyChanged("Started");
                }
            }
        }

        #endregion

        #region Public Methods

        protected TimeredEngineBase(int interval, string name)
        {
            _interval = interval;
            _stopLock = new object();

			this.Timer = new Timer2(1, name); 
            this.Stopped = true;
        }

        public virtual void Start()
        {
            this.Stopped = false;
            this.Started = true;
            this.Timer.Interval = 1; // 1 to fire it inmediately when starting the engine
			this.Timer.Elapsed += this.Timer_Elapsed; // Local object
            this.Timer.Start();
        }

        public virtual void Stop()
        {
            if (this.Started)
            {
                lock (_stopLock)
                {
					this.Timer.Elapsed -= this.Timer_Elapsed;
					this.Timer.Stop();
                    this.Stopped = true;
                    this.Started = false;
                }
            }
        }

        protected abstract void MainMethod();

        #endregion

        #region Event Handlers

        private void Timer_Elapsed(object sender, EventArgs e)
        {
            lock (_stopLock)
            {
				if (this.Started)
				{
					try
					{
						DateTime started = DateTime.Now;
						this.MainMethod();
						int remainingInterval = (int) Math.Max(_interval - DateTime.Now.Subtract(started).TotalMilliseconds, 1);
						this.Timer.Interval = remainingInterval;
					}
					finally
					{
						this.Timer.Start();
					}
				}
            }
        }

        #endregion

        #region IDisposable

        protected bool Disposed { get; private set; }

        private void Dispose(bool disposing)
        {
            if (!this.Disposed)
            {
                this.InternalDispose(disposing);
            }

            this.Disposed = true;
        }

        protected virtual void InternalDispose(bool disposing)
        {
            if (disposing)
            {
                if (this.Started)
                {
                    this.Stop();
                }
                if (this.Timer != null)
                {
                    this.Timer.Dispose();
                }
            }

            // Dispose here only unmanaged objects 
            // Don’t use managed objects here because maybe 
            // they have been finalized already
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~TimeredEngineBase()
        {
            this.Dispose(false);
        }

        #endregion

    }
}